<?php

namespace app\Console;

use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\console\input\Argument;
use think\console\input\Option;
use think\Db;
use think\facade\Env;

//采集图片
class GetImg extends Command {

    public $max_precess = 8;
    public $mpid = 0;
    public $works = [];
    public $new_index = 0;
    public $fields = [
                        'img'=>[   //   /static/book/img/
                                   'select'=>'//*[@id="fmimg"]/img/@src',
                                   'require'=>true,
                                   'node'=>false,
                        ]
    ];

    protected function configure() {
        $this->setName('getimg')
            ->addOption('debug', null, Option::VALUE_REQUIRED, 'debug option')
            ->setDescription('get books img');
    }

    protected function execute(Input $input, Output $output) {
        //throw new \Exception('本任务已完成,请勿重复采集');
        $redis = new \Redis();
        $redis->connect('127.0.0.1', 6379);
        if ($redis->lLen('bookimg') != 0) {
            $output->writeln('队列中存在未完成任务' . $redis->lLen('bookimg'));
            $output->writeln('继续中...');
            //$redis->flushAll('book');exit;
        } else {
            if($input->hasOption('debug')){
                $bug = $input->getOption('debug');
                if($bug == 1){
                    //调试文件 title.php,内容是由于网络等原因没采集到内容的书的id
                    $file = Env::get('app_path') . 'console/debug/img.php';
                    if(file_exists($file)){
                        $ids = include $file;
                        $books = Db::name('bookname')->field('id,img,url')->where('id', 'in', $ids)->where('img is null')->select();
                    }else{
                        throw new \Exception('debug file not exists');
                    }
                }
            }else{
                $books = Db::name('bookname')->field('id,img,url')->where('img is null')->select();
            }
            foreach ($books as $v) {
                $redis->lPush('bookimg', json_encode($v));
            }
        }

        $redis->close();
        swoole_set_process_name('php-think:con');
        $this->mpid = posix_getpid();
        $this->Prun();
        $this->processWait();

        $output->writeln('get start');
    }

    public function Prun() {
        for ($i = 0; $i < $this->max_precess; $i++) {
            $this->CreateProcess();
        }
    }

    public function CreateProcess($index = null) {
        $process = new \swoole_process(function (\swoole_process $worker) use ($index) {
            if (is_null($index)) {
                $index = $this->new_index;
                $this->new_index++;
            }
            $redis = new \Redis();
            $redis->connect('127.0.0.1', '6379', 2);
            swoole_set_process_name(sprintf('php-think:son-%s', $this->new_index));
            for ($j = 0; $j < 10000; $j++) {
                $this->checkMpid($worker); //当前进程pid
                try {
                    $redis->ping();
                } catch (\RedisException $e) {
                    echo $e->getMessage();
                    exit;
                }
                if ($redis->lLen('bookimg') != 0) {
                    $v = $redis->rPop('bookimg');
                } else {
                    dump("endTime: " . date("H:i:s") . 'maybe error or finished');
                    exit;
                }
                if (empty($v)) {
                    exit;
                }
                $v = json_decode($v, true);
                $html = curl_get($v['url']);
                $res = $this->getfield($html, $this->fields);

                $app_path = Env::get('app_path');
                if (empty($res)) {    //没爬到数据
                    file_put_contents($app_path . 'console/debug/img1.php', $v['id'] . ',', FILE_APPEND);
                    echo 'getimg error - ' . $v['id'] . PHP_EOL;
                    continue;
                }
                $img_url = ltrim($res['img'], ' src="');
                $img_url = rtrim($img_url, '"');
                $ext=strrchr($img_url,'.');
                $img = $this->curl_img($img_url);
                $images_dir = Env::get('root_path') . 'public/static/images/';
                $save_dir = date('i', time()) . '/' . date('s', time()) . '/';
                if(!file_exists($images_dir.$save_dir)&&!mkdir($images_dir.$save_dir,0777,true)){
                    throw new \Exception('目录创建失败');
                }
                $new_img = $save_dir . $v['id'] . $ext;
                $fp2=@fopen($images_dir.$new_img,'a');
                fwrite($fp2,$img);
                fclose($fp2);
                try {
                    //Db::execute('set names utf8');
                    //Db::execute("update book_bookname set img='{$new_img}' where id=" .$v['id']);
                    DB::name('bookname')->where('id', $v['id'])->update(['img'=>$new_img]);
                } catch (\PDOException $e) {   //爬到的数据组成sql有误，插入不成功
                    file_put_contents($app_path . 'console/debug/img2.php', $v['id'] . ',', FILE_APPEND);
                    echo 'insert error - ' . $v['id'] . PHP_EOL;
                }

                echo $v['id'] . PHP_EOL;
            }
            //exit('进程任务数量不足');
        }, false, false);
        $pid = $process->start();
        $this->works[$index] = $pid;
        return $pid;
    }

    public function getfield(string $html, array $field = [], $node = false){
        $return = [];
        $document = new \DOMDocument();
        @$document->loadHTML($html);
        //$content = mb_convert_encoding($data, 'UTF-8', 'gbk');
        $xpath = new \DOMXPath($document);
        if (!empty($field) && is_array($field)) {
            try {
                foreach ($field as $name => $select) {
                    $elements[$name] = $xpath->query($select['select']);
                    //print_r($elements[$name][0]->nodeValue);exit;
                    if ($elements[$name]->length != 0) {
                        if ($node) {
                            $return[$name] = $elements[$name][0]->nodeValue;
                        } else {
                            $return[$name] = $document->saveHTML($elements[$name][0]); //本身带标签
                        }
                    }else{
                        return $return;
                    }
                }
                return $return;
            } catch (\Exception $e) {
                exit($e->getMessage());
            }

        }
    }

    public function curl_img(string $url) {

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11");
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:111.222.333.4', 'CLIENT-IP:111.222.333.4'));
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $r = curl_exec($ch);
        curl_close($ch);
        return $r;
    }

    public function checkMpid(&$worker) {
        if (!\swoole_process::kill($this->mpid, 0)) {
            // 这句提示,实际是看不到的.需要写到日志中
            $log = "endTime: " . date("H:i:s") . "--Master process exited, I [{$worker->pid}] also quit\n";
            echo $log;
            $worker->exit();

        }
    }

    public function rebootProcess($ret) {
        $pid = $ret['pid'];
        $index = array_search($pid, $this->works);
        if ($index !== false) {
            $index = intval($index);
            $new_pid = $this->CreateProcess($index);
            echo "rebootProcess: {$index}={$new_pid} Done\n";
            return;
        }
        throw new \Exception('rebootProcess Error: no pid');
    }

//清理僵尸进程

    public function processWait() {
        while (1) {
            if (count($this->works)) {
                $ret = \swoole_process::wait();
                if ($ret) {
                    $this->rebootProcess($ret);
                }
            } else {
                break;
            }
        }
    }
}